#include "ScriptParser.h"
#include "Settings.h"
#include "AnimationInfo.h"
#include "CMSInterface.h"
#include "StorageInterface.h"

_WORK_RESULT ScriptParser::ReadScriptFile(const std::string& script_path , Json::Value& root)
{
	char buf[MAX_BUFFER];
	memset(buf , 0x00 , MAX_BUFFER);

	_WORK_RESULT result;

	if(access(script_path.c_str() ,F_OK) != 0)
	{
		result.Set(WORK_RESULT_ERR_INVALID_PATH
					, std::string("Script file [") + script_path + "] : invalid path");

	}
	else
	{

		FILE *f = fopen( script_path.c_str() , "rb");

		bool ret = false;

		if(f)
		{
			if(!fread(buf , 1 , MAX_BUFFER , f)  > 0 || ! PACKET_BASE::GetRoot(buf , root))
			{
				result.Set(WORK_RESULT_ERR_SCRIPT_STRUCTURE
							, std::string("Script file [") + script_path + "] : invalid structure");

			}

			fclose(f);
		}
		else
		{
			result.Set(WORK_RESULT_ERR_FILE_OPEN
						, std::string("Script file [") + script_path + "] : failed to open");

		}
	}

	return result;
}

_WORK_RESULT ScriptParser::CopyFilesFromS3(_JOB_INFO& job_info)
{
	_WORK_RESULT result;

	std::string remote_path = job_info.GetSrcScriptFilePath(true);
	std::string local_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_script";
	local_path += MATH::GetExt(remote_path);

	if(!StorageInterface::CopyFile(remote_path , local_path))
	{
		result.Set(WORK_RESULT_ERR_INVALID_PATH , remote_path + " -> "+ local_path);
		return result;
	}
	job_info.SetLocalScriptFilePath(local_path);

	remote_path = job_info.GetSrcVideoFilePath(true);
	local_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_org";
	local_path += MATH::GetExt(remote_path);

	if(!StorageInterface::CopyFile(remote_path , local_path))
	{
		result.Set(WORK_RESULT_ERR_INVALID_PATH , remote_path +" -> "+ local_path);
		return result;
	}
	job_info.SetLocalVideoFilePath(local_path);


	return result;
}

double ScriptParser::GetVideoDuration(const std::string& filePath)
{


	std::string out = filePath+".videoDuration";
	std::string cmd = "ffprobe -v quiet -select_streams v -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 " + filePath + " > " + out;
	float duration = -1.0;

	if(_SUCCESS == PROCESS::Execute(_OS_CMD ,cmd , false))
	{
		FILE * f = fopen(out.c_str() , "r");

		if(NULL != f)
		{
			if(!feof(f) )
				fscanf(f , "%f" , &duration);
			fclose(f);
		}

	}
	return duration;
}

double ScriptParser::GetAudioDuration(const std::string& filePath)
{
	std::string out = filePath+".audioDuration";
	std::string cmd = "ffprobe -v quiet -select_streams a -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 " + filePath + " > " + out;
	float duration = -1.0;

	if(_SUCCESS == PROCESS::Execute(_OS_CMD ,cmd , false))
	{



		FILE * f = fopen(out.c_str() , "r");

		if(NULL != f)
		{
			if(!feof(f) )
				fscanf(f , "%f" , &duration);
			fclose(f);
		}

	}
	return duration;
}

_WORK_RESULT ScriptParser::ResaveImageFile(const std::string& path)
{
	_WORK_RESULT result;

	int dotpos = path.rfind("."); 
	if(dotpos < 0)
	{
		result.Set(WORK_RESULT_ERR_INVALID_PATH , std::string("ResaveImageFile : ") + path);	
		return result;
	}

	std::string cmd = "convert " + path + " png32:" + path;

	if(_SUCCESS != PROCESS::Execute(_EXE_FILE , cmd))
	{
		result.Set(WORK_RESULT_ERR_RENDERING_EXE , cmd);
		return result;
	}

	return result;
	
}

_WORK_RESULT ScriptParser::MakeFFMpegCmd( _JOB_INFO& job_info, std::vector<_PROCESS_CMD>& vecCmd, std::string& filterId , std::string& effectIdList , std::string& soundId)
{
	_WORK_RESULT result = CopyFilesFromS3(job_info);
	if(!result.IsSuccess())
		return result;

	Json::Value root;
	std::string cmd;

	result = ReadScriptFile(job_info.GetLocalScriptFilePath() , root);
	if(!result.IsSuccess())
		return result;

	job_info.SetResolution(root);

	
	std::vector<std::string> vecFilePath;
	std::vector<int> vecFileSize;
	for(int i = 0 ; i < root["tempFiles"].size() ; i ++)
	{
		std::string path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_" + PACKET_BASE::GetStringS(root["tempFiles"][i] , "fileName");
		int size = PACKET_BASE::GetIntS(root["tempFiles"][i] , "fileSize");

		vecFilePath.push_back(path);
		vecFileSize.push_back(size);
	}

	if(vecFilePath.size() > 0)
	{

		if(!DivideFile(job_info.GetLocalVideoFilePath() , vecFilePath , vecFileSize))
		{
			result.Set(WORK_RESULT_ERR_INVALID_PATH , "contets file divide error");
			return result;
		}

		int clip_count = 0;
		std::string clip_path;
		for(int i = 0 ; i < vecFilePath.size() ; i ++)
		{
			if(MATH::IsMovie(vecFilePath[i]))
			{
				clip_path = vecFilePath[i]; 
				clip_count ++;
			}
			else if (MATH::IsImage(vecFilePath[i]))
			{	
				_WORK_RESULT result = ResaveImageFile(vecFilePath[i]);
				if(result.result_type != WORK_RESULT_SUCCESS)
					return result;

			}
		}
		
		if(clip_count == 0)
		{
			result.Set(WORK_RESULT_ERR_INVALID_PATH , "There's no movie file in uploaded files");
			return result;
		}
		if(clip_count ==  1)
		{
			job_info.SetLocalVideoFilePath(clip_path);
		}
		else
		{
			std::string base_movie_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_concat.mp4"; 

			std::string concat_input_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_temp_concat";
			FILE * f = fopen(concat_input_path.c_str() , "w");
			if(f)
			{
				for(int i = 0 ; i < vecFilePath.size() ; i ++)
				{
					if(MATH::IsMovie(vecFilePath[i]))
					{
						fprintf(f , "file '%s'\n" , vecFilePath[i].c_str());
					}
				}
				
				fclose(f);
			}
			else
			{
				result.Set(WORK_RESULT_ERR_FILE_OPEN , std::string("TempFile concat input file [") + concat_input_path+"] : failed to open");
				return result;
			}

			cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + (" -f concat -i ") + concat_input_path +" -c:v copy " + base_movie_path;

			if(_SUCCESS != PROCESS::Execute(_EXE_FILE , cmd))
			{
				_WORK_RESULT result;
				result.Set(WORK_RESULT_ERR_RENDERING_EXE , cmd);
				return result;
			}

			job_info.SetLocalVideoFilePath(base_movie_path); 

		} 


	}

	double duration = 0.0;
	for(int i = 0 ; i < root["scenes"].size() ; i ++)
	{
		duration += PACKET_BASE::GetDoubleS(root["scenes"][i] , "duration");
	}

	double video_duration = GetVideoDuration(job_info.GetLocalVideoFilePath());
	double audio_duration = GetAudioDuration(job_info.GetLocalVideoFilePath());

	if(video_duration <= 0.0)
	{
			_WORK_RESULT result;
			result.Set(WORK_RESULT_ERR_RENDERING_EXE , "currupted movie file : can not get duration");
			return result;
	}
	if(duration <=0.0 || duration - 0.1 > video_duration)
	{
		char tmp[100];
		sprintf(tmp , "Invalid duration: %.2f(s) ,  real: %.2f(s) => set real duration" , duration , video_duration);
		LOG::PushJobLog(_REQUIRED , job_info.GetJobId() , tmp);

		duration = video_duration;
	}

	if(duration > Settings::GetSettings()->MAX_MOVIE_TIME)
	{
		char tmp[100];
		sprintf(tmp , "Invalid duration: %.2f(s) >  max: %d(s) => set max duration" , duration , Settings::GetSettings()->MAX_MOVIE_TIME);
		LOG::PushJobLog(_REQUIRED , job_info.GetJobId() , tmp);

		duration = Settings::GetSettings()->MAX_MOVIE_TIME;
	}
	job_info.SetDuration(duration);


	if(audio_duration < duration)
	{
		std::string change_movie_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_soundfill" + MATH::GetExt(job_info.GetLocalVideoFilePath()); 
		char tmp[100];
		sprintf(tmp , "%.2f" , duration );

		if(audio_duration < 0)
		{
			LOG::PushJobLog(_REQUIRED , job_info.GetJobId() , "movie has no sound stream => attach silent sound");
			cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + job_info.GetLocalVideoFilePath() + " -i " + _OBJ_INFO::GetSilentSoundPath() + " -c:v copy -t "+tmp+" " +change_movie_path;
		}
		else
		{
			LOG::PushJobLog(_REQUIRED , job_info.GetJobId() , "movie has short sound stream => add silent sound");

			cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + job_info.GetLocalVideoFilePath() +" -filter_complex \"aevalsrc=0:duration="+tmp+"[no_sound];[0:a][no_sound]concat=n=2:v=0:a=1\" -c:v copy -t "+tmp+" "+change_movie_path;
		}
		vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
		job_info.SetLocalVideoFilePath(change_movie_path);
	}


	std::vector<_OBJ_INFO> vecValue;


	double thumbnailTime = 0.3;


	filterId = "";
	effectIdList = "";
	soundId = "";


	std::set<std::string> setObjectId;

	std::string effectId;
	std::string effectTempFile;
	for(int i = 0 ; i < root["effects"].size() && i < Settings::GetSettings()->MAX_EFFECT ; i ++)
	{
		effectId = PACKET_BASE::GetStringS(root["effects"][i] , "effectId");
		effectTempFile = PACKET_BASE::GetStringS(root["effects"][i] , "tempFile");

		if(effectTempFile.length() > 0) 
		{
			if(effectId.length() == 0 )
				effectIdList += "temp,";
			else
				effectIdList += effectId+",";
		}
		else if(effectId.length() > 0 )
		{
			setObjectId.insert(effectId);

			effectIdList += effectId+",";
		}

	}


	bool appFilter = PACKET_BASE::GetIntS(root , "appFilter");	
	filterId = PACKET_BASE::GetStringS(root , "filterId");
	if(!appFilter && filterId.length() > 0)
	{
		setObjectId.insert(filterId);
	}
	

	bool grayscale = PACKET_BASE::GetIntS(root , "grayscale");

	std::string bgm_id = PACKET_BASE::GetStringS(root , "bgm" , "bgmId");
	bool bgm_replace = PACKET_BASE::GetIntS(root , "bgm" , "replace");
	std::string bgm_tempFile = PACKET_BASE::GetStringS(root , "bgm" , "tempFile");

	if(bgm_id.length() > 0)
	{
		setObjectId.insert(bgm_id);
	}

	if(setObjectId.size() > 0)
	{
		bool filelist_success = false;
		for(int r = 0 ; r < GET_OBJECT_FILE_RETRY_COUNT ; r++)
		{
			filelist_success = CMSInterface::GetObjectInfoList(setObjectId);
			if(filelist_success)
				break;

			printf("GetObjectInfoList failed .. retry\n");

			sleep(2);
		}

		if(!filelist_success)
		{
			result.Set(WORK_RESULT_ERR_INVALID_PATH , "GetObjectInfoList error");
			return result;
		}
	}



	if(bgm_id.length() > 0 || bgm_tempFile.length() > 0)
	{
		std::string bgm_path = "";
		if(bgm_id.length() > 0)
		{
			soundId = bgm_id;

			bgm_path = CMS_OBJECT_DATA::GetBGMPath(bgm_id);
			if(bgm_path.length() == 0)
			{
				result.Set(WORK_RESULT_ERR_SCRIPT_CONTENT , std::string("bgm ID: [") + bgm_id + "] doesn't exist");
				return result;
			}

			if(access( bgm_path.c_str() ,F_OK) != 0)
			{
				result.Set(WORK_RESULT_ERR_INVALID_PATH , std::string("bgm file : [") + bgm_path + "] doesn't exist");
				return result;
			}

		}
		else if(bgm_tempFile.length() > 0)
		{
			soundId = "temp";

			std::string prev_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_" + bgm_tempFile;
			if(access( prev_path.c_str() ,F_OK) != 0)
			{
				result.Set(WORK_RESULT_ERR_INVALID_PATH , std::string("bgm temp file : [") + prev_path + "] doesn't exist");
				return result;
			}

			bgm_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_soundFill_" + bgm_tempFile;

			char tmp[100];
			sprintf(tmp , "%.2f" , job_info.GetDuration() );
			cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + prev_path +" -filter_complex \"aevalsrc=0:duration="+tmp+"[no_sound];[0:a][no_sound]concat=n=2:v=0:a=1\" -c:v copy -t "+tmp+" "+bgm_path;
			vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
		}


		std::string bgm_applied_movie_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_bgm_applied" + MATH::GetExt(job_info.GetLocalVideoFilePath()); 

		char buf[1024];

		cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + job_info.GetLocalVideoFilePath() +" -i " + bgm_path;


		int bgm_fade_duration = Settings::GetSettings()->BGM_FADE_DURATION;
		if(job_info.GetDuration() <= bgm_fade_duration)
			bgm_fade_duration = job_info.GetDuration();

		if(bgm_replace)
		{
			sprintf(buf , " -filter_complex \"[1]afade=t=out:st=%.2f:d=%.2f\"" , job_info.GetDuration() - bgm_fade_duration , bgm_fade_duration -0.1);
		}
		else
		{
			sprintf(buf , " -filter_complex \"[1]volume=0.5,afade=t=out:st=%.2f:d=%.2f[1c];[0:a][1c]amix=inputs=2:duration=first\"" , job_info.GetDuration() - bgm_fade_duration , bgm_fade_duration - 0.1);
		}
		cmd += std::string(buf) + " -c:v copy -shortest " + bgm_applied_movie_path;

		vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
		job_info.SetLocalVideoFilePath(bgm_applied_movie_path);

	}


	if(!appFilter && filterId.length() > 0)
	{
		CMS_FILTER_DATA data = CMSInterface::GetFilterData(filterId);

		if(data.filter_path.length() == 0 && data.curve_path.length() == 0)
		{
			result.Set(WORK_RESULT_ERR_SCRIPT_CONTENT 
				, std::string("filter ID : [") + filterId + "] doesn't exist");

			return result;
		}
		else
		{
			if(data.filter_path.length() > 0 )
			{
				if(access( data.filter_path.c_str() ,F_OK) == 0)
				{
					_OBJ_INFO oi(data.filter_path , data.blending_type , data.opacity);
					vecValue.push_back(oi);
				}
				else
				{
					result.Set(WORK_RESULT_ERR_INVALID_PATH
						, std::string("filter file : [") + data.filter_path + "] doesn't exist");

					return result;
				}
			}

			if(data.curve_path.length() > 0)
			{
				if(access( data.curve_path.c_str() ,F_OK) == 0)
				{
					_OBJ_INFO oi(data.curve_path);
					vecValue.push_back(oi);
				}
				else
				{
					result.Set(WORK_RESULT_ERR_INVALID_PATH
						, std::string("curve file : [") + data.curve_path + "] doesn't exist");

					return result;
				}

			}
		}


	}
	
	for(int i = 0 ; i < root["effects"].size() && i < Settings::GetSettings()->MAX_EFFECT ; i ++)
	{
		PACKET_EFFECT e(root["effects"][i]);

		_OBJ_INFO oi = e.GetObjectInfo(job_info.GetJobId());

		
		if(oi.obj_path.length() == 0)
		{
			if(e.GetTempFile().length() > 0)
				result.Set(WORK_RESULT_ERR_SCRIPT_CONTENT , std::string("Effect TempFile : [") + e.GetTempFile() + "] doesn't exist");
			else 
				result.Set(WORK_RESULT_ERR_SCRIPT_CONTENT , std::string("Effect ID : [") + e.GetEffectID() + "] doesn't exist");

			return result;
		}

		if(access(oi.obj_path.c_str() ,F_OK) != 0)
		{
			result.Set(WORK_RESULT_ERR_INVALID_PATH
				, std::string("Effect file : [") + e.GetEffectID() + " : " +  oi.obj_path + "] doesn't exist");

			return result;
		}


		std::string colorMixBlend = e.GetColorMixBlend();
		if(colorMixBlend.length() > 0)
		{

			char buf[1024];
			sprintf(buf , "%d" , vecValue.size());

			_COLOR color = e.GetColorMixColor();
			if(!color.IsValid())
			{
				result.Set(WORK_RESULT_ERR_INVALID_PATH
					, "Invalid effect mix color");

				return result;
			}

			std::string mix_image_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_miximage_" + buf + IMG_EXT;
			cmd =  std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + _OBJ_INFO::GetBlankImgPath() + " -vf \"scale=100:100,drawbox=x=0:y=0:w=100:h=100:color="+color.GetHexString()+":thickness=50\" -vcodec png -pix_fmt rgb24 " + mix_image_path;
			vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));

			std::string mixed_path;
			cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD); 

			if(MATH::IsImage(oi.obj_path))
			{
				mixed_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_mixed_" + buf + IMG_EXT;

				cmd += " -i " + oi.obj_path;
			}
			else
			{
				mixed_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_mixed_" + buf + ORG_EXT;

				sprintf(buf , " -t %.2f -i %s -r %d -t %.2f -loop 1 " , oi.GetTime().GetDuration() , oi.obj_path.c_str() ,Settings::GetSettings()->FRAME_RATE , oi.GetTime().GetDuration());
				cmd += buf;
			}

			cmd += std::string(" -i ") + mix_image_path + " -filter_complex ";

			sprintf(buf , " \"[0]scale=%.0f:%.0f,setsar=1[0m];[1]scale=%.0f:%.0f,setsar=1,lutrgb=a=0.0[1m];[0m][1m]blend=all_mode='%s':shortest=1\" " 
				, oi.GetSize().GetWidth() , oi.GetSize().GetHeight()  , oi.GetSize().GetWidth() , oi.GetSize().GetHeight()  , colorMixBlend.c_str());
			cmd += std::string(buf) + " -vcodec png -q 0 " + mixed_path;

			vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));

			oi.obj_path = mixed_path;

		}

		if(e.GetAnimationSize() > 0)
		{
			AnimationInfo ai(oi);

			int a = 0;
			for(; a < e.GetAnimationSize() ; a ++)
			{
				if(!ai.CalcAnimation(e.GetAnimationInfo(a)))
					break;
			}

			if(a == e.GetAnimationSize())
			{
				char tmp_buf[1024 * 2];
				std::string cmd;

				sprintf(tmp_buf , "%s_%d" , job_info.GetJobId().c_str() , vecValue.size());
				std::string new_effect_name = tmp_buf;

				bool isImage = MATH::IsImage(oi.obj_path);

				if(!isImage)
				{

					cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD);
					sprintf(tmp_buf , " -r %d -i %s -f image2 -vframes %d" ,Settings::GetSettings()->FRAME_RATE ,oi.obj_path.c_str(), ai.GetFrameCount()); 
					cmd += std::string(tmp_buf) + " " + Settings::GetSettings()->TMP_DIR + new_effect_name+"_ext_%%d"+ IMG_EXT;

					


					
					vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
				}

				_OBJ_INFO *frame_list = ai.GetFrameList();

				cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " +  _OBJ_INFO::GetBlankImgPath() + " ";
				int count = 0;
				for(int f = 0 ; f < ai.GetFrameCount() ; f ++)
				{
					_OBJ_INFO &o = frame_list[f];

					std::string obj_path;

					if(isImage)
					{
						obj_path = o.obj_path;
					}
					else
					{
						sprintf(tmp_buf , "%d" , f+1);
						obj_path = Settings::GetSettings()->TMP_DIR + new_effect_name+"_ext_" + tmp_buf + IMG_EXT;
					}

					MATH::ConvertFilterPath(obj_path);

					sprintf(tmp_buf , "-vf \"movie='%s',scale=%.0f:%.0f,rotate='%.2f:ow=rotw(%.2f):oh=roth(%.2f):fillcolor=0x00000000',lutrgb=a=val*%.1f[%d];[in]scale=%d:%d[in2];[in2][%d]overlay=(%.0f-w/2):(%.0f-h/2)\" -vcodec png %s%s_%d%s "
								, obj_path.c_str()
								, o.GetSize().GetWidth() , o.GetSize().GetHeight()
								, o.GetRotation() , o.GetRotation() , o.GetRotation() 
								, o.GetAlpha()
								, f 
								, job_info.GetHighResolution().GetWidth() , job_info.GetHighResolution().GetHeight()
								, f
								, o.GetPosition().GetX() , o.GetPosition().GetY()
								, Settings::GetSettings()->TMP_DIR , new_effect_name.c_str() ,  f+1 , IMG_EXT
								);


					if(strlen(tmp_buf) + cmd.length() > MAX_CMD_LENGTH)
					{
						vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
						cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + _OBJ_INFO::GetBlankImgPath() + " " + tmp_buf;
						count = 1;

					}
					else
					{
						cmd += tmp_buf;
						count ++;

						if(count == Settings::GetSettings()->MAX_IMAGE || f == ai.GetFrameCount()-1 )
						{
							vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
							cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + _OBJ_INFO::GetBlankImgPath() + " ";
							count = 0;
						}
					}

				} 

				sprintf(tmp_buf , "%s -f image2 -r %d -i %s%s_%%%%d%s -vcodec copy %s%s%s" 
					, Settings::GetSettings()->RENDERER_HEAD_CMD
					, Settings::GetSettings()->FRAME_RATE 
					, Settings::GetSettings()->TMP_DIR , new_effect_name.c_str()
					, IMG_EXT
					, Settings::GetSettings()->TMP_DIR , new_effect_name.c_str() 
					, ORG_EXT);

				vecCmd.push_back(_PROCESS_CMD(tmp_buf , _EXE_FILE));

				if(!Settings::IsDebugMode())
				{
					cmd = std::string(DELETE_CMD) + " " + Settings::GetSettings()->TMP_DIR + new_effect_name + "_*" + IMG_EXT;
					vecCmd.push_back(_PROCESS_CMD(cmd , _OS_CMD));
				}

				oi.SetAnimationTmpFileName(job_info.GetHighResolution().GetWidth() , job_info.GetHighResolution().GetHeight() , new_effect_name + ORG_EXT);	

			} 

		} 

		vecValue.push_back(oi);

	} 





	if(vecValue.size() == 0)
	{
		char tmp_buf[1024];
		std::string cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + job_info.GetLocalVideoFilePath();

		if('T' == Settings::GetSettings()->ADD_WATERMARK || 't' == Settings::GetSettings()->ADD_WATERMARK)
		{
			int watermark_height = 0;
			int watermark_margin = 0;

			if(job_info.GetHighResolution().GetWidth() == job_info.GetHighResolution().GetHeight())
			{
				watermark_height = (int)( (double)job_info.GetHighResolution().GetHeight() / (double)Settings::GetSettings()->WATERMARK_HEIGHT_RATIO );
				watermark_margin = (int)( (double)job_info.GetHighResolution().GetHeight() / (double)Settings::GetSettings()->WATERMARK_MARGIN_RATIO );
			}
			else
			{
				watermark_height = (int)( (double)job_info.GetHighResolution().GetHeight() / (double)Settings::GetSettings()->WATERMARK_HEIGHT_RATIO2 );
				watermark_margin = (int)( (double)job_info.GetHighResolution().GetHeight() / (double)Settings::GetSettings()->WATERMARK_MARGIN_RATIO2 );
			}

			cmd += std::string(" -i ") + Settings::GetSettings()->ADD_DIR + Settings::GetSettings()->WATERMARK_FILE_NAME;

			if(MATH::IsImage(Settings::GetSettings()->WATERMARK_FILE_NAME))
			{
				sprintf(tmp_buf , " -filter_complex \"[0]scale=%d:%d,setsar=1.0[0m];[1]scale=%d*iw/ih:%d[1m];[0m][1m]overlay=main_w-w-%d:main_h-h-%d\" " 
					, job_info.GetHighResolution().GetWidth() , job_info.GetHighResolution().GetHeight()
					, watermark_height , watermark_height 
					, watermark_margin , watermark_margin );
			}
			else
			{
				sprintf(tmp_buf , " -filter_complex \"[0]scale=%d:%d,setsar=1.0[0m];[1]scale=%d*iw/ih:%d[1m];[0m][1m]overlay=main_w-w-%d:main_h-h-%d:shortest=1\" " 
					, job_info.GetHighResolution().GetWidth() , job_info.GetHighResolution().GetHeight()
					, watermark_height , watermark_height 
					, watermark_margin , watermark_margin );
			}


		}
		else
		{
			sprintf(tmp_buf , " -filter_complex \"[0]scale=%d:%d,setsar=1.0\" " , job_info.GetHighResolution().GetWidth() , job_info.GetHighResolution().GetHeight() );
		}

		char tmp[100];
		sprintf(tmp , "%.2f" , job_info.GetDuration() );

		cmd += std::string(tmp_buf) + Settings::GetSettings()->GetRendererTailCmd(job_info.GetHighBV() ,job_info.GetHighBA() ) + " -t " + tmp + " " + Settings::GetSettings()->TMP_DIR + job_info.GetJobId();
		
		if('T' == Settings::GetSettings()->ADD_TRAILER || 't' == Settings::GetSettings()->ADD_TRAILER)
		{
			cmd += OUT_EXT;
		}
		else
		{
			cmd += std::string("_final") + OUT_EXT; 
		}

		vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
	}
	else
	{
		int exe_count = vecValue.size() % ONCE_EFFECT_COUNT == 0 ? vecValue.size() / ONCE_EFFECT_COUNT : vecValue.size() / ONCE_EFFECT_COUNT + 1;

		char tmp[1024];

		for(int i = 0 ; i < exe_count ; i ++)
		{
			std::string cmd;
			
			_WORK_RESULT each_result = MakeEachCommand(vecValue , exe_count , i , job_info , cmd  , grayscale , bgm_replace);
			if(!each_result.IsSuccess())
				return each_result;

			vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
		}
	}

	if('T' == Settings::GetSettings()->ADD_TRAILER || 't' == Settings::GetSettings()->ADD_TRAILER)
	{
		result = AddTrailerCmd(job_info , vecCmd);
		if(!result.IsSuccess())
			return result;
	}

	AddThumbnailCmd(job_info , thumbnailTime, vecCmd);

	std::string high_file = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_final" + OUT_EXT;

	char buf[1024];

	std::string middle_file = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_final_middle" + OUT_EXT;

	cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + high_file;
	memset(buf , 0x00 , 1024);

	sprintf(buf , " -s %dx%d", job_info.GetMiddleResolution().GetWidth() , job_info.GetMiddleResolution().GetHeight());
	cmd += std::string(buf) + Settings::GetSettings()->GetRendererTailCmd(job_info.GetMiddleBV() , job_info.GetMiddleBA()) + middle_file;

	vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));

	std::string low_file = "";
	if(job_info.HasLowResolution())
	{
		low_file = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_final_low" + OUT_EXT;

		cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + high_file;
		memset(buf , 0x00 , 1024);

		sprintf(buf , " -s %dx%d", job_info.GetLowResolution().GetWidth() , job_info.GetLowResolution().GetHeight());
		cmd += std::string(buf) + Settings::GetSettings()->GetRendererTailCmd(job_info.GetLowBV() , job_info.GetLowBA()) + low_file;

		vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
	}


	memset(buf , 0x00 , 1024);
	sprintf(buf , "%d", Settings::GetSettings()->SEGMENT_TIME);

	cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + high_file
		+ " -c copy -bsf:v h264_mp4toannexb -map 0 -flags global_header -f segment"
		+ " -segment_list " + Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_high.m3u8"
		+ " -segment_time " + buf
		+ " " + Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_high_%%03d.ts";
	
	vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));

	cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + middle_file
		+ " -c copy -bsf:v h264_mp4toannexb -map 0 -flags global_header -f segment"
		+ " -segment_list " + Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_middle.m3u8"
		+ " -segment_time " + buf
		+ " " + Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_middle_%%03d.ts";
	
	vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));

	if(job_info.HasLowResolution())
	{
		cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " + low_file
			+ " -c copy -bsf:v h264_mp4toannexb -map 0 -flags global_header -f segment"
			+ " -segment_list " + Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_low.m3u8"
			+ " -segment_time " + buf
			+ " " + Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_low_%%03d.ts";
		
		vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));
	}

	MakeM3u8IndexFile(job_info);


	return result;

}

void ScriptParser::MakeM3u8IndexFile(_JOB_INFO& ji)
{
	std::string path = Settings::GetSettings()->TMP_DIR + ji.GetJobId()  + ".m3u8";

	FILE * f = fopen(path.c_str() , "w");
	if(f)
	{
		fprintf(f , "#EXTM3U\n");
		if(ji.HasLowResolution()) 
		{
			fprintf(f , "#EXT-X-STREAM-INF:BANDWIDTH=%d\n" , ji.GetLowBV()*1000);
			fprintf(f , "%s_low.m3u8\n" , ji.GetJobId().c_str());
		}
		fprintf(f , "#EXT-X-STREAM-INF:BANDWIDTH=%d\n" , ji.GetMiddleBV()*1000);
		fprintf(f , "%s_middle.m3u8\n" , ji.GetJobId().c_str());
		fprintf(f , "#EXT-X-STREAM-INF:BANDWIDTH=%d\n" , ji.GetHighBV()*1000);
		fprintf(f , "%s_high.m3u8\n" , ji.GetJobId().c_str());

		fclose(f);
	}
}

std::string ScriptParser::MakeVideoInputCommand(_OBJ_INFO& oi)
{
	std::string cmd;

	
	if(oi.GetType() == _EFFECT || oi.GetType() == _TEMP)
	{
		char tmp[1024];
		if(MATH::IsImage(oi.obj_path))
		{
			sprintf(tmp , " -itsoffset %.2f -t %.2f -loop 1 -i %s" , oi.GetTime().GetStart() ,oi.GetTime().GetDuration() + Settings::GetSettings()->FRAME_INTERVAL , oi.obj_path.c_str());
		}
		else
		{
			sprintf(tmp , " -itsoffset %.2f -t %.2f -i %s" , oi.GetTime().GetStart() , oi.GetTime().GetDuration() , oi.obj_path.c_str());
		}

		cmd = tmp;
	}
	else if(oi.GetType() == _BLEND)
	{
		if(MATH::IsImage(oi.obj_path))
			cmd += std::string(" -loop 1 -i ") + oi.obj_path;
		else
			cmd += std::string(" -i ") + oi.obj_path;
	}

	return cmd;

}

std::string ScriptParser::MakeVideoFormerFilterCommand(_JOB_INFO& ji , _OBJ_INFO& oi , int input_index)
{
	std::string cmd;
	char tmp[1024];

	if(oi.GetType() == _EFFECT || oi.GetType() == _TEMP)
	{


		sprintf(tmp , "[%d]scale=%.0f:%.0f,rotate='%.2f:ow=rotw(%.2f):oh=roth(%.2f):fillcolor=0x00000000'[%dm];"
			, input_index 
			, oi.GetSize().GetWidth() , oi.GetSize().GetHeight()
			, oi.GetRotation() , oi.GetRotation() , oi.GetRotation() 
			, input_index);


		cmd = tmp;
	}
	else if(oi.GetType() == _BLEND)
	{
		if(oi.GetBlending().length() > 0)
		{
			sprintf(tmp , "[%d]scale=%d:%d,setsar=1.0,format=pix_fmts=rgb24[%dm];" , input_index , ji.GetHighResolution().GetWidth() , ji.GetHighResolution().GetHeight()  , input_index);
			cmd += tmp;
		}
		else
		{
			sprintf(tmp , "[%d]scale=%d:%d[%dm];" ,input_index , ji.GetHighResolution().GetWidth() , ji.GetHighResolution().GetHeight()  , input_index);
			cmd += tmp;
		}

	}

	return cmd;

}

std::string ScriptParser::MakeVideoLatterFilterCommand(_OBJ_INFO& oi , int input_index , std::string& prev_mark  , int accum_len)
{
	char tmp[1024];

	std::string cmd;
	if(accum_len == 0)
		cmd = "["+prev_mark+"]";
	else
		cmd = "["+prev_mark+"];["+prev_mark+"]";

	if(oi.GetType() == _EFFECT || oi.GetType() == _TEMP)
	{
		sprintf(tmp , "[%dm]overlay=(%.0f-w/2):(%.0f-h/2):repeatlast=0"
			,  input_index , oi.GetPosition().GetX() , oi.GetPosition().GetY());

		cmd += tmp;

		sprintf(tmp , "%dk" , input_index);
		prev_mark = tmp;
	}
	else if(oi.GetType() == _BLEND)
	{
		if(oi.GetBlending().length() > 0)
		{
			cmd += "format=pix_fmts=rgb24[conv_"+prev_mark+"];[conv_"+prev_mark+"]";

			sprintf(tmp , "[%dm]blend=all_mode='%s':all_opacity=%.1f:shortest=1,format=pix_fmts=rgb24" 
				,input_index , oi.GetBlending().c_str() , oi.GetOpacity() 
				,prev_mark.c_str() , prev_mark.c_str() );
			cmd += tmp;
		}
		else
		{
			sprintf(tmp , "[%dm]overlay=(main_w-w)/2:(main_h-h)/2:repeatlast=0" , input_index);
			cmd += tmp;
		}

		sprintf(tmp , "%dk" , input_index);
		prev_mark = tmp;

	}
	else if(oi.GetType() == _CURVE && oi.obj_path.length() > 0)
	{
		MATH::ConvertFilterPath(oi.obj_path);

		cmd += "curves=psfile='"+oi.obj_path+"'";
		prev_mark = "curve_" + prev_mark;
	}

	return cmd;

}


_WORK_RESULT ScriptParser::MakeEachCommand(const std::vector<_OBJ_INFO>& vecValue, const int exe_count ,const int index ,  _JOB_INFO& job_info , std::string& cmd , const bool grayscale , const bool nosound)
{

	_WORK_RESULT result;

	char tmp[1024];

	std::string org_file;
	if(index == 0)
		org_file = job_info.GetLocalVideoFilePath();
	else
	{
		sprintf(tmp , "%s_%d" , job_info.GetJobId().c_str() , index-1);
		org_file = std::string(Settings::GetSettings()->TMP_DIR) + tmp + ORG_EXT;
	}

	std::string out_file;
	if(index == exe_count - 1)
	{
		out_file = Settings::GetSettings()->TMP_DIR + job_info.GetJobId();

		if('T' == Settings::GetSettings()->ADD_TRAILER || 't' == Settings::GetSettings()->ADD_TRAILER)
			out_file += OUT_EXT;
		else
			out_file += std::string("_final") + OUT_EXT; 

	}
	else
	{
		sprintf(tmp , "%s_%d" , job_info.GetJobId().c_str() , index);
		out_file = std::string(Settings::GetSettings()->TMP_DIR) + tmp + ORG_EXT;
	}

	cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -i " +  org_file;

	std::string input_cmd;
	std::string former_filter_cmd;
	std::string latter_filter_cmd;

	
	const int start_index = index * ONCE_EFFECT_COUNT;

	std::string prev_mark = "0m";
	int input_index = 1;

	bool include_blending = false;
	bool filter_count = 0;

	std::vector<_OBJ_INFO> vecSound;


	for(int i = 1 ; i <= ONCE_EFFECT_COUNT && (i + start_index) <= vecValue.size() ; i ++)
	{
		_OBJ_INFO  oi =  vecValue[i + start_index -1];


		input_cmd += MakeVideoInputCommand(oi);
		former_filter_cmd += MakeVideoFormerFilterCommand(job_info , oi , input_index);
		latter_filter_cmd += MakeVideoLatterFilterCommand(oi , input_index, prev_mark , latter_filter_cmd.length());

		if(!oi.IsNonInput())
			input_index++;

		if(oi.GetType() == _BLEND && oi.GetBlending().length() > 0)
			include_blending = true;
		if(oi.IsFilter())
			filter_count ++;

		if(!nosound && oi.HasSound())
		{
			if(oi.sound_path.length() > 0 &&  access( oi.sound_path.c_str() ,F_OK) == 0)
				vecSound.push_back(oi);
			else
			{
				result.Set(WORK_RESULT_ERR_INVALID_PATH
					, std::string("sound file : [") + oi.sound_path + "] doesn't exist" );

				return result;
			}
		}

	}

	if(0 == index && grayscale)
	{
		sprintf(tmp , "[0]format=gray,scale=%d:%d,setsar=1.0,fps=%d[0m];" , job_info.GetHighResolution().GetWidth() , job_info.GetHighResolution().GetHeight()  ,Settings::GetSettings()->FRAME_RATE);
	}
	else
	{
		sprintf(tmp , "[0]scale=%d:%d,setsar=1.0,fps=%d[0m];" , job_info.GetHighResolution().GetWidth() , job_info.GetHighResolution().GetHeight() ,Settings::GetSettings()->FRAME_RATE);
	}

	former_filter_cmd = tmp + former_filter_cmd;

	if(filter_count > 0)
	{
		latter_filter_cmd += "["+prev_mark+"];["+prev_mark+"]format=pix_fmts=yuv420p";
	}

	if(index == exe_count - 1 && ('T' == Settings::GetSettings()->ADD_WATERMARK || 't' == Settings::GetSettings()->ADD_WATERMARK))
	{
		input_cmd += std::string(" -i ") + Settings::GetSettings()->ADD_DIR + Settings::GetSettings()->WATERMARK_FILE_NAME;

		int watermark_height = 0;
		int watermark_margin = 0;

		if(job_info.GetHighResolution().GetWidth() == job_info.GetHighResolution().GetHeight())
		{
			watermark_height = (int)( (double)job_info.GetHighResolution().GetHeight() / (double)Settings::GetSettings()->WATERMARK_HEIGHT_RATIO );
			watermark_margin = (int)( (double)job_info.GetHighResolution().GetHeight() / (double)Settings::GetSettings()->WATERMARK_MARGIN_RATIO );
		}
		else
		{
			watermark_height = (int)( (double)job_info.GetHighResolution().GetHeight() / (double)Settings::GetSettings()->WATERMARK_HEIGHT_RATIO2 );
			watermark_margin = (int)( (double)job_info.GetHighResolution().GetHeight() / (double)Settings::GetSettings()->WATERMARK_MARGIN_RATIO2 );
		}


		char tmp_buf[1024];

		if(MATH::IsImage(Settings::GetSettings()->WATERMARK_FILE_NAME))
		{
			sprintf(tmp_buf , "[prev_watermark];[%d]scale=%d*iw/ih:%d[%dm];[prev_watermark][%dm]overlay=main_w-w-%d:main_h-h-%d" 
					, input_index , watermark_height , watermark_height , input_index
					,input_index , watermark_margin , watermark_margin );
		}
		else
		{
			sprintf(tmp_buf , "[prev_watermark];[%d]scale=%d*iw/ih:%d[%dm];[prev_watermark][%dm]overlay=main_w-w-%d:main_h-h-%d:shortest=1" 
					, input_index , watermark_height , watermark_height , input_index
					,input_index , watermark_margin , watermark_margin );
		}

		latter_filter_cmd += tmp_buf;

		input_index ++; 
	}



	if(vecSound.size() > 0)
	{
		std::string sound_former;

		std::string sound_latter = ";[0:a]";

		int applied_count = 0;
		for(int s = 0 ; s < vecSound.size() ; s ++)
		{
			_OBJ_INFO & oi =  vecSound[s];

			if(oi.GetTime().GetDuration() > Settings::GetSettings()->SOUND_FADE_DURATION)
			{
				sprintf(tmp , " -t %.2f -i " , oi.GetTime().GetDuration());
				input_cmd += tmp + oi.sound_path; 
				
				sprintf(tmp , ";aevalsrc=0:duration=%.2f[h_silent_%d];aevalsrc=0:duration=%.2f[t_silent_%d];[%d]afade=out:st=%.2f:d=%.2f[%dac];[h_silent_%d][%dac][t_silent_%d]concat=n=3:v=0:a=1[sound_%d]"
								, oi.GetTime().GetStart() , applied_count + 1 
								, job_info.GetDuration() - oi.GetTime().GetStart() -oi.GetTime().GetDuration()  , applied_count + 1 
								, input_index , oi.GetTime().GetDuration() - Settings::GetSettings()->SOUND_FADE_DURATION , Settings::GetSettings()->SOUND_FADE_DURATION , input_index
								, applied_count + 1 , input_index , applied_count + 1, applied_count + 1 );
				sound_former += tmp;

				sprintf(tmp , "[sound_%d]" , applied_count + 1);
				sound_latter += tmp;

				input_index ++;
				applied_count ++;
			}
		}

		if(applied_count > 0)
		{
			sprintf(tmp , "amix=inputs=%d:duration=first,volume=%d" , applied_count + 1, applied_count + 1); 
			sound_latter += tmp;

			latter_filter_cmd += sound_former + sound_latter;
		}


	} 


	sprintf(tmp , " -t %.2f " , job_info.GetDuration() ); 
	cmd += input_cmd + " -filter_complex \"" + 	former_filter_cmd + latter_filter_cmd +"\" " + Settings::GetSettings()->GetRendererTailCmd(job_info.GetHighBV() ,job_info.GetHighBA() ) + tmp +  out_file;

	return result;

}


int ScriptParser::AddThumbnailCmd( _JOB_INFO& job_info , const double thumbnailTime,  std::vector<_PROCESS_CMD>& vecCmd)
{
	std::string HIGH_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_final" + OUT_EXT;
	std::string web_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_web" + THUMBNAIL_EXT;
	std::string app_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_mobile" + THUMBNAIL_EXT;

	char cmd[1024];

	sprintf(cmd , "%s -ss %.2f -i %s -f image2 -vframes 1  -s %dx%d %s -vframes 1  -s %dx%d %s" 
		, Settings::GetSettings()->RENDERER_HEAD_CMD
		, thumbnailTime
		, HIGH_path.c_str()
		 , job_info.GetMiddleResolution().GetWidth() , job_info.GetMiddleResolution().GetHeight() , web_path.c_str() 
		 , job_info.GetHighResolution().GetWidth() , job_info.GetHighResolution().GetHeight() , app_path.c_str() 
		);
	vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));


	return vecCmd.size();

}

_WORK_RESULT ScriptParser::AddTrailerCmd(_JOB_INFO& job_info , std::vector<_PROCESS_CMD>& vecCmd )
{
	_WORK_RESULT result;
	


	double last_frame_time = job_info.GetDuration() - (Settings::GetSettings()->FRAME_INTERVAL * 10);

	char tmp_buf[1024];

	std::string final_path		= Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_final" + OUT_EXT;

	std::string HIGH_path		= Settings::GetSettings()->TMP_DIR + job_info.GetJobId() +  OUT_EXT; 

	std::string img_path		= std::string(Settings::GetSettings()->TMP_DIR) + job_info.GetJobId() + "_last" + IMG_EXT;

	std::string trailer_path	= std::string(Settings::GetSettings()->TMP_DIR) + job_info.GetJobId() + "_trailer" + OUT_EXT;


	sprintf(tmp_buf , "%s -ss %.2f -i %s -f image2 -update 1  %s" 
		, Settings::GetSettings()->RENDERER_HEAD_CMD 	, last_frame_time , HIGH_path.c_str() , img_path.c_str() );
	vecCmd.push_back(_PROCESS_CMD(tmp_buf , _EXE_FILE));

	std::string cmd;

	std::string trailerFileName;
	if(job_info.GetHighResolution().GetWidth() == job_info.GetHighResolution().GetHeight())
		trailerFileName = Settings::GetSettings()->TRAILER_FILE_NAME;						
	else		
		trailerFileName = Settings::GetSettings()->TRAILER_FILE_NAME2;						

	sprintf(tmp_buf , "%s -i %s%s -t %.2f -loop 1 -i %s  -filter_complex "
		,Settings::GetSettings()->RENDERER_HEAD_CMD , Settings::GetSettings()->ADD_DIR , trailerFileName.c_str() , Settings::GetSettings()->TRAILER_FADE_TIME , img_path.c_str() );
	cmd = tmp_buf;

	sprintf(tmp_buf ,"\"[0]scale=%d:%d,setsar=1.0[0m];[1]fade=t=out:st=0:d=%.2f:alpha=1[1m];[0m][1m]overlay=0:0\"" , job_info.GetHighResolution().GetWidth() , job_info.GetHighResolution().GetHeight() 
																													, Settings::GetSettings()->TRAILER_FADE_TIME);

	cmd += tmp_buf;
	cmd += std::string(Settings::GetSettings()->GetRendererTailCmd(job_info.GetHighBV() ,job_info.GetHighBA() )) + trailer_path;

	vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));

	std::string concat_input_path = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_trailer_concat";
	FILE * f = fopen(concat_input_path.c_str() , "w");
	if(f)
	{
		fprintf(f , "file   '%s'\n" , HIGH_path.c_str()); 	
		fprintf(f , "file   '%s'\n" , trailer_path.c_str());	
		fclose(f);

		cmd = std::string(Settings::GetSettings()->RENDERER_HEAD_CMD) + " -f concat -i " + concat_input_path + " -c copy -movflags +faststart -force_key_frames 00:00:00.000 -ss 0.036 " + final_path;
		vecCmd.push_back(_PROCESS_CMD(cmd , _EXE_FILE));	

	}
	else
	{
		result.Set(WORK_RESULT_ERR_FILE_OPEN , std::string("Trailer concat input file [") + concat_input_path + "] : failed to open");
		printf("%s\n" , result.msg.c_str());
	}

	job_info.SetDuration(job_info.GetDuration() + Settings::GetSettings()->TRAILER_TIME);


	return result;
}





bool ScriptParser::MakeInputListFile(const std::string& inputlist_path , const std::vector<std::string>& vecPath)
{
	FILE * f = fopen(inputlist_path.c_str()  , "w");
	if(!f)
		return false;

	for(int i = 0 ; i < vecPath.size() ; i ++)
	{
		fprintf(f , "file '%s'\n" , vecPath[i].c_str());
	}

	fclose(f);

	return true;
}

bool ScriptParser::DivideFile(const std::string& in_path , const std::vector<std::string>& vecPath, const std::vector<int>& vecSize)
{
	FILE * f_in = fopen(in_path.c_str() , "rb");

	if(!f_in)
		return false;

	bool ret = true;
	BYTE buf[MAX_BUFFER];

	for(int i = 0 ; i < vecPath.size() ; i ++)
	{
		FILE * f_out = fopen(vecPath[i].c_str() , "wb");
		if(!f_out)
		{
			ret = false;
			break;
		}

		const int file_size = vecSize[i];
		int read_size = 0;
		while(read_size < file_size)
		{
			memset(buf , 0x00 , MAX_BUFFER);

			if(read_size + MAX_BUFFER > file_size)
			{
				fread(buf , 1 , file_size - read_size , f_in);
				fwrite(buf , 1 , file_size - read_size , f_out);

				read_size = file_size;
			}
			else
			{
				fread(buf , 1 , MAX_BUFFER , f_in);
				fwrite(buf , 1 , MAX_BUFFER , f_out);

				read_size += MAX_BUFFER;
			}

		}



		fclose(f_out);

	}

	
	fclose(f_in);

	return ret;

}
